﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;

namespace MICZ.CodeBuilder.Commons.ExpressionExtensions
{
    /// <summary>
    /// 扩展方法
    /// </summary>
    public static class ExpressionExtensions
    {

        #region ===========================对象的克隆拷贝============================
        /// <summary>
        /// 对象的克隆(深度拷贝)
        /// </summary>
        /// <typeparam name="T">要拷贝的对象的数据类型</typeparam>
        /// <param name="RealObject">需要拷贝的对象</param>
        /// <returns>返回拷贝后的对象</returns>
        public static T Clone<T>(T RealObject)
        {
            if (RealObject == null)
            {
                return default(T);
            }
            using (Stream objectStream = new MemoryStream())
            {
                //利用 System.Runtime.Serialization序列化与反序列化完成引用对象的复制  
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(objectStream, RealObject);
                objectStream.Seek(0, SeekOrigin.Begin);
                return (T)formatter.Deserialize(objectStream);
            }
        }
        /// <summary>
        /// 对象深度拷贝，将此对象拷贝给令一个对象，使用此方法的前提，两类型必须是继承关系或者是数据类型相同，否则将抛出异常
        /// </summary>
        /// <typeparam name="T">原有的对象</typeparam>
        /// <typeparam name="B">克隆结果的数据类型B</typeparam>
        /// <param name="RealObject">克隆后的结果</param>
        /// <returns></returns>
        public static B CloneToOther<T, B>(T RealObject)
        {
            if (RealObject == null)
            {
                return default(B);
            }
            using (Stream objectStream = new MemoryStream())
            {
                //利用 System.Runtime.Serialization序列化与反序列化完成引用对象的复制  
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(objectStream, RealObject);
                objectStream.Seek(0, SeekOrigin.Begin);
                return (B)formatter.Deserialize(objectStream);
            }
        }
        /// <summary>
        /// 将List集合进行克隆（深度拷贝）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="listToClone"></param>
        /// <returns></returns>
        public static IList<T> Clone<T>(this IList<T> listToClone) where T : ICloneable
        {
            if (listToClone == null)
            {
                return default(IList<T>);
            }
            return listToClone.Select(item => (T)item.Clone()).ToList();
        }
        #endregion

        #region ===================字符串转lambda==========================
        /// <summary>
        /// 将字符的Where语句，动态创建出Experession 表达式数据结构树
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="strWhere"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> StrToExpressionLambda<T>(string strWhere)
        {
            //if(!MrSorft.Young.Validate.ValidatedCopyright.validatedGrant())
            //{
            //      System.Web.HttpContext.Current.Response.Redirect("http://grant.ynyes.cn/");
            //}
            strWhere = strWhere.ToLower().Replace("  ", " ");
            //两个空字符串换成一个
            string[] operater = { "!=", "<>", ">=", "<=", ">", "<", "==", "=", "like '%" };
            //切开  and (.....) 
            List<string> list_where = new List<string>();
            List<Expression<Func<T, bool>>> list_expression = new List<Expression<Func<T, bool>>>();
            strWhere = RectifyStrAnd(strWhere, list_where);
            if (strWhere.Trim() != "")
            {
                StrToExpressionLambda<T>(operater, list_expression, strWhere);
            }
            foreach (string item in list_where)
            {
                StrToExpressionLambda<T>(operater, list_expression, item);
            }
            Expression<Func<T, bool>> whereLambda = True<T>();
            for (int i = 0; i < list_expression.Count; i++)
            {
                whereLambda = whereLambda.And(list_expression[i]);
            }
            return whereLambda;
        }

        /// <summary>
        /// 将字符串 to LambdaList
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="operater"></param>
        /// <param name="list_expression"></param>
        /// <param name="item"></param>
        private static void StrToExpressionLambda<T>(string[] operater, List<Expression<Func<T, bool>>> list_expression, string item)
        {
            Expression<Func<T, bool>> whereLambda = True<T>();
            int index_or = item.IndexOf(" or(");
            #region 非复杂组合
            if (index_or > 0)
            {

                string where1 = item.Substring(0, index_or);
                string where2 = item.Substring(index_or + 4);
                string[] strWheres = { where1, where2 };
                if (strWheres.Length >= 2)
                {
                    //and or 混合运算 一次Or
                    for (int i = 0; i < strWheres.Length; i++)
                    {
                        if (i == 0)
                        {
                            //纯and 运算
                            GetExpressionNotOr<T>(strWheres[i], ref whereLambda, operater);
                        }
                        else
                        {
                            string[] ors = strWheres[i].Split(new string[] { "or(" }, StringSplitOptions.RemoveEmptyEntries);
                            Expression<Func<T, bool>> whereLambdaTempOr = null;
                            if (ors.Length > 1)//剩余部分切开后 还有 or
                            {
                                whereLambdaTempOr = StrToExpressionLambda<T>(strWheres[i]);
                            }
                            else
                            {
                                GetExpressionNotOr<T>(strWheres[i].Replace(")", ""), ref whereLambdaTempOr, operater);
                            }
                            if (whereLambdaTempOr != null)
                            {
                                whereLambda = whereLambda.Or(whereLambdaTempOr);
                            }
                        }
                    }
                }
            }
            else
            {
                //纯and 运算
                GetExpressionNotOr<T>(item, ref whereLambda, operater);

            }
            #endregion
            list_expression.Add(whereLambda);
        }

        /// <summary>
        /// 切割 and(.......)
        /// </summary>
        /// <param name="strWhere"></param>
        /// <param name="list_where"></param>
        /// <returns></returns>
        private static string RectifyStrAnd(string strWhere, List<string> list_where)
        {
            string returnstr = "";
            int index_and = strWhere.IndexOf(" and (");
            int index_or = strWhere.IndexOf(" or(");
            if (index_and < index_or)
            {
                if (index_and >= 0)
                {
                    string _top = strWhere.Substring(0, index_and);
                    strWhere = strWhere.Substring(index_and + 6);
                    int left_bracket = 0;
                    int right_bracket = 0;
                    string tempWhere = "";
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < strWhere.Length; i++)
                    {
                        if (strWhere[i] == '(')
                        {
                            left_bracket++;
                        }
                        else if (strWhere[i] == ')')
                        {
                            right_bracket++;
                        }
                        sb.Append(strWhere[i]);
                        if (left_bracket == right_bracket - 1)
                        {
                            string where_1 = sb.ToString().Remove(sb.Length - 1, 1);
                            list_where.Add(where_1);
                            string where_2 = strWhere.Replace(where_1 + ")", "");
                            tempWhere = RectifyStrAnd(where_2, list_where);
                            returnstr = " " + _top + tempWhere;
                        }
                    }
                }
                else if (index_or > 0)
                {
                    //stock_headquarters>0) or(is_del<>0)
                    string[] st2 = strWhere.Split(new string[] { " or(" }, StringSplitOptions.RemoveEmptyEntries);
                    if (st2.Length == 2)
                    {
                        strWhere.Replace(")", "");
                        list_where.Add(strWhere);
                    }
                }
            }
            else
            {
                return strWhere;
            }
            return returnstr;
        }
        /// <summary>
        /// 创建 表达式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="strWhere"></param>
        /// <param name="whereLambda"></param>
        /// <param name="operater"></param>
        private static void GetExpressionNotOr<T>(string strWhere, ref Expression<Func<T, bool>> whereLambda, string[] operater)
        {
            var parameter = Expression.Parameter(typeof(T), "c");
            string thisopt = "";
            #region 不含Or运算的方法
            string[] wheres = strWhere.Split(new string[] { " and " }, StringSplitOptions.RemoveEmptyEntries);
            int index = 0;
            for (int i = 0; i < wheres.Length; i++)
            {
                try
                {
                    #region 生成结构树
                    string where = wheres[i];
                    string[] values = { };
                    int operaterj = 0;
                    for (int j = 0; j < operater.Length; j++)
                    {
                        values = where.Split(new string[] { operater[j] }, StringSplitOptions.RemoveEmptyEntries);
                        if (values.Length != 2)
                        {
                            operaterj++;
                            continue;
                        }
                        else
                        {
                            thisopt = operater[j];
                            break;
                        }
                    }
                    if (operaterj == operater.Length)
                    {
                        //未找到运算符号
                        continue;
                    }
                    //根据属性名获取属性
                    var property = typeof(T).GetProperty(values[0].Replace("(", "").Trim());
                    if (property == null)
                    {
                        continue;
                    }
                    //创建一个访问属性的表达式
                    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                    Type tpyes = typeof(T).GetProperty(values[0].Replace("(", "").Trim()).PropertyType;
                    string value = values[1].Replace(")", "").Replace("%", "").Replace("'", "").Trim();
                    Expression constants;
                    switch (tpyes.FullName)
                    {
                        case "System.Int32":
                            constants = Expression.Constant(Convert.ToInt32(value.Trim()), tpyes);
                            break;
                        case "System.DateTime":
                            DateTime dtime = Convert.ToDateTime(value);
                            constants = Expression.Constant(dtime, tpyes);
                            break;
                        case "System.Decimal":
                            constants = Expression.Constant(Convert.ToDecimal(value.Trim()), tpyes);
                            break;
                        case "System.Double":
                            constants = Expression.Constant(Convert.ToDouble(value.Trim()), tpyes);
                            break;
                        case "System.String":
                            constants = Expression.Constant(Convert.ToString(value.Trim()), tpyes);
                            break;
                        default:
                            continue;
                    }
                    var bin = Expression.Equal(propertyAccess, constants);
                    switch (thisopt)
                    {
                        case "!=":
                        case "<>":
                            bin = Expression.NotEqual(propertyAccess, constants);
                            break;
                        case ">=":
                            bin = Expression.GreaterThanOrEqual(propertyAccess, constants);
                            break;
                        case "<=":
                            bin = Expression.LessThanOrEqual(propertyAccess, constants);
                            break;
                        case ">":
                            bin = Expression.GreaterThan(propertyAccess, constants);
                            break;
                        case "<":
                            bin = Expression.LessThan(propertyAccess, constants);
                            break;
                        case "==":
                        case "=":
                            bin = Expression.Equal(propertyAccess, constants);
                            break;
                        case "like '%":
                            break;
                        default:
                            continue;
                    }
                    if (thisopt == "like '%")
                    {
                        var lambda = GetConditionExpression<T>(value, values[0].Trim());
                        if (whereLambda == null)
                        {
                            whereLambda = lambda;
                        }
                        else
                        {
                            whereLambda = whereLambda.And(lambda);
                        }

                    }
                    else
                    {
                        var lambda = Expression.Lambda<Func<T, Boolean>>(bin, parameter);
                        if (whereLambda == null)
                        {
                            whereLambda = lambda;
                        }
                        else
                        {
                            whereLambda = whereLambda.And(lambda);
                        }
                    }
                    index++;
                    #endregion
                }
                catch
                {
                }
            }
            #endregion
        }
        #endregion

        public static Predicate<T> ToPredicate<T>(this Func<T, bool> source)
        {
            Predicate<T> result = new Predicate<T>(source);
            return result;
        }

        public static Expression<Func<T, bool>> CreatedLambda<T>(string propertyName, string value, string oparater = "==")
        {
            var parameter = Expression.Parameter(typeof(T), "c");
            //根据属性名获取属性
            var property = typeof(T).GetProperty(propertyName);
            if (property == null)
            {
                return null;
            }
            var propertyAccess = Expression.MakeMemberAccess(parameter, property);
            Type tpyes = typeof(T).GetProperty(propertyName).PropertyType;
            Expression constants;
            switch (tpyes.FullName)
            {
                case "System.Int32":
                    constants = Expression.Constant(Convert.ToInt32(value.Trim()), tpyes);
                    break;
                case "System.DateTime":
                    DateTime dtime = Convert.ToDateTime(value);
                    constants = Expression.Constant(dtime, tpyes);
                    break;
                case "System.Decimal":
                    constants = Expression.Constant(Convert.ToDecimal(value.Trim()), tpyes);
                    break;
                case "System.Double":
                    constants = Expression.Constant(Convert.ToDouble(value.Trim()), tpyes);
                    break;
                case "System.String":
                    constants = Expression.Constant(Convert.ToString(value.Trim()), tpyes);
                    break;
                default:
                    constants = null;
                    break;
            }
            var bin = Expression.Equal(propertyAccess, constants);
            switch (oparater.ToLower())
            {
                case "!=":
                case "<>":
                    bin = Expression.NotEqual(propertyAccess, constants);
                    break;
                case ">=":
                    bin = Expression.GreaterThanOrEqual(propertyAccess, constants);
                    break;
                case "<=":
                    bin = Expression.LessThanOrEqual(propertyAccess, constants);
                    break;
                case ">":
                    bin = Expression.GreaterThan(propertyAccess, constants);
                    break;
                case "<":
                    bin = Expression.LessThan(propertyAccess, constants);
                    break;
                case "==":
                case "=":
                    bin = Expression.Equal(propertyAccess, constants);
                    break;
                case "contains":
                    return GetConditionExpression<T>(value, propertyName);
            }
            return Expression.Lambda<Func<T, bool>>(bin, parameter);
        }

        /// <summary>
        /// 在WhereLambda中 添加IS_DELETE 字段的过滤，设置 IS_DELETE=0，若此对象不含IS_DELETE字段，则返回原有的表达式
        /// </summary>
        /// <typeparam name="T">数据类型T</typeparam>
        /// <param name="whereLambda">lambda条件</param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> LambdaAddConditionIsDel<T>(Expression<Func<T, bool>> whereLambda)
        {
            var parameter = Expression.Parameter(typeof(T), "c");
            //根据属性名获取属性
            var property = typeof(T).GetProperty("IS_DELETE");
            if (property == null)
            {
                return whereLambda;
            }
            //创建一个访问属性的表达式
            var propertyAccess = Expression.MakeMemberAccess(parameter, property);
            Type tpyes = typeof(T).GetProperty("IS_DELETE").PropertyType;
            Expression constants = Expression.Constant(0, tpyes);
            var bin = Expression.Equal(propertyAccess, constants);
            var lambda = Expression.Lambda<Func<T, Boolean>>(bin, parameter);
            if (whereLambda == null)
            {
                whereLambda = lambda;
            }
            else
            {
                whereLambda = whereLambda.And<T>(lambda);
            }
            return whereLambda;
        }


        /// <summary>
        /// 创建string.Contains方法，为指定对象的指定字段，动态创建出Contains方法
        /// </summary>
        /// <typeparam name="T">数据类型T</typeparam>
        /// <param name="_keywords">关键词</param>
        /// <param name="fieldName">字段名称</param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> GetConditionExpression<T>(string _keywords, string fieldName)
        {
            ParameterExpression left = Expression.Parameter(typeof(T), "c");
            Expression expression = Expression.Constant(false);
            expression = Expression.Call(Expression.Property(left, typeof(T).GetProperty(fieldName)), typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), Expression.Constant(_keywords));
            Expression<Func<T, bool>> finalExpression = Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left });
            return finalExpression;
        }
        /// <summary>
        /// 获得指定字段的Lambda表达式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="intFieldName"></param>
        /// <returns></returns>
        public static Expression<Func<T, int>> CreatedLambdaAccessField<T>(string intFieldName)
        {
            var parameter = Expression.Parameter(typeof(T), "c");
            //根据属性名获取属性
            var property = typeof(T).GetProperty(intFieldName);
            if (property == null)
            {
                throw new Exception(intFieldName);
            }
            //创建一个访问属性的表达式
            var propertyAccess = Expression.MakeMemberAccess(parameter, property);
            var lambda = Expression.Lambda<Func<T, int>>(propertyAccess, parameter);

            return lambda;
        }
        /// <summary>
        /// 设置指定lambda 默认为Ture
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <returns></returns>
        public static Expression<Func<T, bool>> True<T>() { return c => true; }

        /// <summary>
        /// 设置指定lambda 默认为False
        /// </summary>
        /// <typeparam name="T">数据类型T</typeparam>
        /// <returns></returns>
        public static Expression<Func<T, bool>> False<T>() { return c => false; }

        /// <summary>
        /// 将两个表达式树 使用 And 合并成同一个表达式树
        /// </summary>
        /// <typeparam name="T">对象数据类型</typeparam>
        /// <param name="first">第一个表达式树</param>
        /// <param name="second">第二个表达式树</param>
        /// <returns>合并后的结果</returns>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.And);
        }

        /// <summary>
        /// 将两个表达式树 使用 Or 合并成同一个表达式树
        /// </summary>
        /// <typeparam name="T">对象数据类型</typeparam>
        /// <param name="first">第一个表达式树</param>
        /// <param name="second">第二个表达式树</param>
        /// <returns>合并后的结果</returns>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.Or);
        }

        /// <summary>
        /// 进行合并
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="first"></param>
        /// <param name="second"></param>
        /// <param name="merge"></param>
        /// <returns></returns>
        private static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
        {
            var map = first.Parameters.Select((c, i) => new { c, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.c);
            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
        }
    }
    class ParameterRebinder : ExpressionVisitor
    {
        private readonly Dictionary<ParameterExpression, ParameterExpression> map;

        public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
        {
            this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
        }

        public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
        {
            return new ParameterRebinder(map).Visit(exp);
        }

        protected override Expression VisitParameter(ParameterExpression p)
        {
            ParameterExpression replacement;
            if (map.TryGetValue(p, out replacement))
            {
                p = replacement;
            }
            return base.VisitParameter(p);
        }
    }
}
